{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "9326712e",
   "metadata": {},
   "source": [
    "# Intra-factory Transport\n",
    "## Capacitated Pickup and Delivery Problem with Time Windows"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "2cb694f7",
   "metadata": {},
   "outputs": [],
   "source": [
    "from cuopt import routing\n",
    "from cuopt import distance_engine\n",
    "import cudf\n",
    "import numpy as np\n",
    "import pandas as pd"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "382afbd9",
   "metadata": {},
   "source": [
    "Factory automation allows companies to raise the quality and consistency of manufacturing processes while also allowing human workers to focus on safer, less repetitive tasks that have higher cognitive and creative demands.\n",
    "\n",
    "In this scenario we have a set of intra-factory transport orders to move products at various stages in the assembly process from one processing station to another. Each station represents a particular type of manufacturing process and a given product may need to visit each processing station more than once. Multiple autonomous mobile robots (AMRs) with a fixed capacity will execute pickup and delivery orders between target locations, all with corresponding time_windows."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "c3bc4ad4",
   "metadata": {},
   "source": [
    "### Problem Details:\n",
    "- 4 Locations each with an associated demand\n",
    "    - 1 Start Location for AMRs\n",
    "\n",
    "    - 3 Process Stations\n",
    "\n",
    "- 3 AMRs with associated capacity"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "e6090764",
   "metadata": {},
   "source": [
    "- Hours of operation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "5d12f05d",
   "metadata": {},
   "outputs": [],
   "source": [
    "factory_open_time = 0\n",
    "factory_close_time = 100"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "e67a05ed",
   "metadata": {},
   "source": [
    "![waypoint_graph.png not found](./images/waypoint_graph.png \"Waypoint Graph\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "d90ba90d",
   "metadata": {},
   "source": [
    "### Waypoint Graph"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "6febdb57",
   "metadata": {},
   "source": [
    "#### Compressed Sparse Row (CSR) representation of above weighted waypoint graph.\n",
    "For details on the CSR encoding of the above graph see the [cost_matrix_and_waypoint_graph_creation.ipynb](https://github.com/NVIDIA/cuopt-examples/blob/branch-25.02/intra-factory_transport/cost_matrix_and_waypoint_graph_creation.ipynb) notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "2c824c99",
   "metadata": {},
   "outputs": [],
   "source": [
    "offsets = np.array([0, 1, 3, 7, 9, 11, 13, 15, 17, 20, 22])\n",
    "edges =   np.array([2, 2, 4, 0, 1, 3, 5, 2, 6, 1, 7, 2, 8, 3, 9, 4, 8, 5, 7, 9, 6, 8])\n",
    "weights = np.array([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 2, 1, 2])"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "dbfcfa33",
   "metadata": {},
   "source": [
    "#### Select specific waypoints in the graph as target locations.\n",
    "In this case we would like the AMRs to begin from waypoint 0 and service locations 4, 5, and 6."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "4e08f664",
   "metadata": {},
   "outputs": [],
   "source": [
    "target_locations = np.array([0, 4, 5, 6])"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "7af883ad",
   "metadata": {},
   "source": [
    "### Cost Matrix"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "52bdc1d0",
   "metadata": {},
   "source": [
    "#### Use cuOpt to calculate the corresponding cost matrix and transit time matrix.\n",
    "\n",
    "Lets assume transit time is same as cost matrix."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "9975bf1a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Waypoint graph node to time matrix index mapping \n",
      "{np.int64(0): 0, np.int64(4): 1, np.int64(5): 2, np.int64(6): 3}\n",
      "\n",
      "     0    1    2    3\n",
      "0  0.0  6.0  4.0  6.0\n",
      "1  6.0  0.0  4.0  6.0\n",
      "2  4.0  4.0  0.0  4.0\n",
      "3  6.0  6.0  4.0  0.0\n"
     ]
    }
   ],
   "source": [
    "waypoint_graph = distance_engine.WaypointMatrix(\n",
    "    offsets,\n",
    "    edges,\n",
    "    weights\n",
    ")\n",
    "cost_matrix = waypoint_graph.compute_cost_matrix(target_locations)\n",
    "transit_time_matrix = cost_matrix.copy(deep=True)\n",
    "target_map = {v:k for k, v in enumerate(target_locations)}\n",
    "index_map = {k:v for k, v in enumerate(target_locations)}\n",
    "print(f\"Waypoint graph node to time matrix index mapping \\n{target_map}\\n\")\n",
    "print(cost_matrix)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c18199d8",
   "metadata": {},
   "source": [
    "##### Important Notes:\n",
    "- If the user already has square cost matrix and transit time matrix, it can be used directly.\n",
    "\n",
    "- If there are different kinds of vehicles (e.g., bike, car, truck) requiring different cost and transit time matrices:\n",
    "    - Provide vehicle type index while setting cost/transit time matrix.\n",
    "    - Set vehicle type for each vehicle in ``vehicle_data``.\n",
    "    - Share all the vehicle types for all vehicles.\n",
    "         \n",
    "         \n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "4ed911ff",
   "metadata": {},
   "source": [
    "### Transport Orders"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "4265c03a",
   "metadata": {},
   "source": [
    "Setup Transport Order Data"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "d7d7536d",
   "metadata": {},
   "source": [
    "The transport orders dictate the movement of parts from one area of the factory to another.  In this example nodes 4, 5, and 6 represent the processing stations that parts must travel between and deliveries to node 0 represent the movement of parts off the factory floor."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "72b715c7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>pickup_location</th>\n",
       "      <th>delivery_location</th>\n",
       "      <th>order_demand</th>\n",
       "      <th>earliest_pickup</th>\n",
       "      <th>latest_pickup</th>\n",
       "      <th>pickup_service_time</th>\n",
       "      <th>earliest_delivery</th>\n",
       "      <th>latest_delivery</th>\n",
       "      <th>delivery_serivice_time</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>4</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>10</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>45</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>5</td>\n",
       "      <td>6</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>20</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>45</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>6</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>30</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>45</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>6</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>10</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>45</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5</td>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>20</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>45</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>30</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>45</td>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   pickup_location  delivery_location  order_demand  earliest_pickup  \\\n",
       "0                4                  5             1                0   \n",
       "1                5                  6             1                0   \n",
       "2                6                  0             1                0   \n",
       "3                6                  5             1                0   \n",
       "4                5                  4             1                0   \n",
       "5                4                  0             1                0   \n",
       "\n",
       "   latest_pickup  pickup_service_time  earliest_delivery  latest_delivery  \\\n",
       "0             10                    2                  0               45   \n",
       "1             20                    2                  0               45   \n",
       "2             30                    2                  0               45   \n",
       "3             10                    2                  0               45   \n",
       "4             20                    2                  0               45   \n",
       "5             30                    2                  0               45   \n",
       "\n",
       "   delivery_serivice_time  \n",
       "0                       2  \n",
       "1                       2  \n",
       "2                       2  \n",
       "3                       2  \n",
       "4                       2  \n",
       "5                       2  "
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "transport_order_data = cudf.DataFrame({\n",
    "    \"pickup_location\":       [4,  5,  6,  6,  5,  4],\n",
    "    \"delivery_location\":     [5,  6,  0,  5,  4,  0],\n",
    "    \"order_demand\":          [1,  1,  1,  1,  1,  1],\n",
    "    \"earliest_pickup\":       [0,  0,  0,  0,  0,  0],\n",
    "    \"latest_pickup\":         [10, 20, 30, 10, 20, 30],\n",
    "    \"pickup_service_time\":   [2,  2,  2,  2,  2,  2],\n",
    "    \"earliest_delivery\":     [0,  0,  0,  0,  0,  0],\n",
    "    \"latest_delivery\":       [45, 45, 45, 45, 45, 45],\n",
    "    \"delivery_serivice_time\":[2,  2,  2,  2,  2,  2]\n",
    "})\n",
    "transport_order_data"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "f2aaf28a",
   "metadata": {},
   "source": [
    "### AMR Data"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "a4e5e749",
   "metadata": {},
   "source": [
    "Set up AMR fleet data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "9e17e899",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>carrying_capacity</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>robot_ids</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "           carrying_capacity\n",
       "robot_ids                   \n",
       "0                          2\n",
       "1                          2"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n_robots = 2\n",
    "robot_data = {\n",
    "    \"robot_ids\": [i for i in range(n_robots)],\n",
    "    \"carrying_capacity\":[2, 2]\n",
    "}\n",
    "robot_data = cudf.DataFrame(robot_data).set_index('robot_ids')\n",
    "robot_data"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "31db9053",
   "metadata": {},
   "source": [
    "### cuOpt DataModel View"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "731fdcbe",
   "metadata": {},
   "source": [
    "Setup the routing.DataModel."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "2e765325",
   "metadata": {},
   "outputs": [],
   "source": [
    "n_locations = len(cost_matrix)\n",
    "n_vehicles = len(robot_data)\n",
    "\n",
    "# a pickup order and a delivery order are distinct with additional pad for the depot with 0 demand\n",
    "n_orders = len(transport_order_data) * 2\n",
    "data_model = routing.DataModel(n_locations, n_vehicles, n_orders)\n",
    "data_model.add_cost_matrix(cost_matrix)\n",
    "data_model.add_transit_time_matrix(transit_time_matrix)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "7f8f10e8",
   "metadata": {},
   "source": [
    "\n",
    "#### Set the Per-Order Demand\n",
    "\n",
    "From the perspective of the cuOpt solver_settings, each distinct transaction (pickup order or delivery order) are treated separately with demand for pickup denoted as positive and the corresponding delivery treated as negative demand."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "c936b137",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0     1\n",
       "1     1\n",
       "2     1\n",
       "3     1\n",
       "4     1\n",
       "5     1\n",
       "6    -1\n",
       "7    -1\n",
       "8    -1\n",
       "9    -1\n",
       "10   -1\n",
       "11   -1\n",
       "Name: order_demand, dtype: int64"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# This is the number of parts that needs to be moved.\n",
    "raw_demand = transport_order_data[\"order_demand\"]\n",
    "\n",
    "# When dropping off parts we want to remove one unit of demand from the robot.\n",
    "drop_off_demand = raw_demand * -1\n",
    "\n",
    "# Create pickup and delivery demand.\n",
    "order_demand = cudf.concat([raw_demand, drop_off_demand], ignore_index=True)\n",
    "\n",
    "order_demand"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "87c2d9f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Add the capacity dimension.\n",
    "data_model.add_capacity_dimension(\"demand\", order_demand, robot_data['carrying_capacity'])"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "48706e31",
   "metadata": {},
   "source": [
    "#### Setting Order Locations"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "281bcd93",
   "metadata": {},
   "source": [
    "Set the order locations and pickup and delivery pairs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "1d325f4b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0     1\n",
      "1     2\n",
      "2     3\n",
      "3     3\n",
      "4     2\n",
      "5     1\n",
      "6     2\n",
      "7     3\n",
      "8     0\n",
      "9     2\n",
      "10    1\n",
      "11    0\n",
      "dtype: int64\n"
     ]
    }
   ],
   "source": [
    "pickup_order_locations = cudf.Series([target_map[loc] for loc in transport_order_data['pickup_location'].to_arrow().to_pylist()])\n",
    "delivery_order_locations = cudf.Series([target_map[loc] for loc in transport_order_data['delivery_location'].to_arrow().to_pylist()])\n",
    "order_locations = cudf.concat([pickup_order_locations, delivery_order_locations], ignore_index=True)\n",
    "\n",
    "print(order_locations)\n",
    "\n",
    "# add order locations\n",
    "data_model.set_order_locations(order_locations)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "9389060b",
   "metadata": {},
   "source": [
    "#### Mapping Pickups to Deliveries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "064978ca",
   "metadata": {},
   "outputs": [],
   "source": [
    "# IMPORTANT NOTE : Pickup and delivery pairs are indexed into the order locations array.\n",
    "npair_orders = int(len(order_locations)/2)\n",
    "pickup_orders = cudf.Series([i for i in range(npair_orders)])\n",
    "delivery_orders = cudf.Series([i + npair_orders for i in range(npair_orders)])\n",
    "# Add pickup and delivery pairs.\n",
    "data_model.set_pickup_delivery_pairs(pickup_orders, delivery_orders)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "ef21d42d",
   "metadata": {},
   "source": [
    "#### Time Windows"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "b3f328e3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# create earliest times\n",
    "vehicle_earliest_time = cudf.Series([factory_open_time] * n_vehicles)\n",
    "order_time_window_earliest = cudf.concat([transport_order_data[\"earliest_pickup\"], transport_order_data[\"earliest_delivery\"]], ignore_index=True)\n",
    "\n",
    "# create latest times\n",
    "vehicle_latest_time = cudf.Series([factory_close_time] * n_vehicles)\n",
    "order_time_window_latest = cudf.concat([transport_order_data[\"latest_pickup\"], transport_order_data[\"latest_delivery\"]], ignore_index=True)\n",
    "\n",
    "# create service times\n",
    "order_service_time = cudf.concat([transport_order_data[\"pickup_service_time\"], transport_order_data[\"delivery_serivice_time\"]], ignore_index=True)\n",
    "\n",
    "# add time window constraints\n",
    "data_model.set_order_time_windows(order_time_window_earliest, order_time_window_latest)\n",
    "data_model.set_order_service_times(order_service_time)\n",
    "data_model.set_vehicle_time_windows(vehicle_earliest_time, vehicle_latest_time)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "b0d06888",
   "metadata": {},
   "source": [
    "### CuOpt SolverSettings"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "e3e08235",
   "metadata": {},
   "source": [
    "Set up routing.SolverSettings."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "a6babc11",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "solver_settings = routing.SolverSettings()\n",
    "\n",
    "# solver_settings will run for given time limit.  Larger and/or more complex problems may require more time.\n",
    "solver_settings.set_time_limit(5)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "854e9519",
   "metadata": {},
   "source": [
    "### Solution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "28a05ace",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cost for the routing in time:  32.0\n",
      "Vehicle count to complete routing:  2\n",
      "    route  arrival_stamp  truck_id  location      type\n",
      "0       0            0.0         0         0     Depot\n",
      "1       1            4.0         0         2    Pickup\n",
      "2       3           10.0         0         3    Pickup\n",
      "3       7           12.0         0         3  Delivery\n",
      "4       2           14.0         0         3    Pickup\n",
      "5       9           20.0         0         2  Delivery\n",
      "6       8           26.0         0         0  Delivery\n",
      "7       0           28.0         0         0     Depot\n",
      "8       0            0.0         1         0     Depot\n",
      "9       4            4.0         1         2    Pickup\n",
      "10      0           10.0         1         1    Pickup\n",
      "11     10           12.0         1         1  Delivery\n",
      "12      5           14.0         1         1    Pickup\n",
      "13      6           20.0         1         2  Delivery\n",
      "14     11           26.0         1         0  Delivery\n",
      "15      0           28.0         1         0     Depot\n"
     ]
    }
   ],
   "source": [
    "routing_solution = routing.Solve(data_model, solver_settings)\n",
    "if routing_solution.get_status() == 0:\n",
    "    print(\"Cost for the routing in time: \", routing_solution.get_total_objective())\n",
    "    print(\"Vehicle count to complete routing: \", routing_solution.get_vehicle_count())\n",
    "    print(routing_solution.route)\n",
    "else:\n",
    "    print(\"NVIDIA cuOpt Failed to find a solution with status : \", routing_solution.get_status())"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "4f6c5067",
   "metadata": {},
   "source": [
    "#### Converting Solution to Waypoint Graph"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "1dbba138",
   "metadata": {},
   "source": [
    "Because we maintained the mapping between cost matrix indices and locations in the waypoint graph, we can now convert our solution to reference the nodes in the waypoint graph corresponding to the selected target locations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "e0d98709",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    route  arrival_stamp  truck_id  location      type  order_array_index\n",
      "0       0            0.0         0         0     Depot                  0\n",
      "1       5            4.0         0         2    Pickup                  1\n",
      "2       6           10.0         0         3    Pickup                  3\n",
      "3       6           12.0         0         3  Delivery                  7\n",
      "4       6           14.0         0         3    Pickup                  2\n",
      "5       5           20.0         0         2  Delivery                  9\n",
      "6       0           26.0         0         0  Delivery                  8\n",
      "7       0           28.0         0         0     Depot                  0\n",
      "8       0            0.0         1         0     Depot                  0\n",
      "9       5            4.0         1         2    Pickup                  4\n",
      "10      4           10.0         1         1    Pickup                  0\n",
      "11      4           12.0         1         1  Delivery                 10\n",
      "12      4           14.0         1         1    Pickup                  5\n",
      "13      5           20.0         1         2  Delivery                  6\n",
      "14      0           26.0         1         0  Delivery                 11\n",
      "15      0           28.0         1         0     Depot                  0\n"
     ]
    }
   ],
   "source": [
    "target_loc_route = [index_map[loc] for loc in routing_solution.route['location'].to_arrow().to_pylist()]\n",
    "routing_solution.route['order_array_index'] = routing_solution.route['route']\n",
    "routing_solution.route['route'] = target_loc_route\n",
    "print(routing_solution.route)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "bba4accd",
   "metadata": {},
   "source": [
    "#### Convert Routes from Target Location-Based Routes to Waypoint-Level Routes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "c13cfbf3",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Target location level route for robot 0:\n",
      "0    0\n",
      "1    5\n",
      "2    6\n",
      "3    6\n",
      "4    6\n",
      "5    5\n",
      "6    0\n",
      "7    0\n",
      "Name: route, dtype: int64\n",
      "\n",
      "\n",
      "Waypoint level route for robot 0:\n",
      "    waypoint_sequence waypoint_type\n",
      "0                   0             w\n",
      "1                   2             w\n",
      "2                   5        Pickup\n",
      "3                   5             w\n",
      "4                   8             w\n",
      "5                   9             w\n",
      "6                   6        Pickup\n",
      "7                   6      Delivery\n",
      "8                   6        Pickup\n",
      "9                   6             w\n",
      "10                  9             w\n",
      "11                  8             w\n",
      "12                  5      Delivery\n",
      "13                  5             w\n",
      "14                  2             w\n",
      "15                  0      Delivery\n",
      "16                  0         Depot\n",
      "\n",
      "\n",
      "Target location level route for robot 1:\n",
      "8     0\n",
      "9     5\n",
      "10    4\n",
      "11    4\n",
      "12    4\n",
      "13    5\n",
      "14    0\n",
      "15    0\n",
      "Name: route, dtype: int64\n",
      "\n",
      "\n",
      "Waypoint level route for robot 1:\n",
      "    waypoint_sequence waypoint_type\n",
      "0                   0             w\n",
      "1                   2             w\n",
      "2                   5        Pickup\n",
      "3                   5             w\n",
      "4                   8             w\n",
      "5                   7             w\n",
      "6                   4        Pickup\n",
      "7                   4      Delivery\n",
      "8                   4        Pickup\n",
      "9                   4             w\n",
      "10                  7             w\n",
      "11                  8             w\n",
      "12                  5      Delivery\n",
      "13                  5             w\n",
      "14                  2             w\n",
      "15                  0      Delivery\n",
      "16                  0         Depot\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "unique_robot_ids = routing_solution.route['truck_id'].unique()\n",
    "all_routes = routing_solution.get_route()\n",
    "\n",
    "for robot in unique_robot_ids.to_arrow().to_pylist():\n",
    "    route = all_routes[all_routes['truck_id']==robot]\n",
    "    waypoint_route = waypoint_graph.compute_waypoint_sequence(target_locations, route)\n",
    "    print(f\"Target location level route for robot {robot}:\\n{all_routes[all_routes['truck_id']==robot]['route']}\\n\\n\")\n",
    "    print(f\"Waypoint level route for robot {robot}:\\n{waypoint_route}\\n\\n\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "cuopt",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
